/**
 * Copyright 2007-2008. Chongqing First Information & Network Co., Ltd. All
 * rights reserved. <a>http://www.cqfirst.com.cn</a>
 */
package com.cqcis.uip.base.framework.common;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.mina.common.IoSession;

import com.cqcis.uip.base.framework.FrameworkConstants;
import com.cqcis.uip.base.framework.UIPServiceException;
import com.cqcis.uip.base.framework.core.BeforeSet;
import com.cqcis.uip.base.framework.core.Route;
import com.cqcis.uip.base.framework.core.TaskContext;
import com.cqcis.uip.base.framework.core.impl.RouteImpl;
import com.cqcis.uip.base.framework.utils.ClassUtil;
import com.cqcis.uip.base.framework.utils.ConcurrentTaskQueue;
import com.cqcis.uip.base.framework.utils.StringUtil;
import com.cqcis.uip.base.framework.utils.TaskUtil;
import com.cqcis.uip.base.framework.utils.TimeUtil;

/**
 *  任务描述: 
 *    一个任务包括一个任务ID, 及其任务相关的属性, 如环节ID、接口ID
 *    
 * @author huangmy
 * @date   2007-12-13
 */
public class Task implements Serializable {
	private static final long serialVersionUID = 1L;
	private String taskId;        /*任务ID, 可能为订单号*/  
    private String uniqueTo; // 惟一标识该task
    private String uniqueFrom; // 惟一标识该task
    private Object attachement;   /*任务关联的实例*/
    private String message;    
    private Map<String, Object> attributes;
    private More more;            /*测试是否有下一个任务节点的接口*/
    private int againCount;     /*标志 again 已经设置*/
    private boolean again;        /*是否再次处理*/
    private long waitTime;        /*等待处理时间*/ 
    private boolean exclusive;    /*如果重复处理，是否独占当前处理环境*/
    private State state = State.WAIT;          /*状态*/
    private Route route = new RouteImpl();/*路由*/
    private List<StateHistory> stateHistory = Collections.synchronizedList(new ArrayList<StateHistory>());
    private boolean generateResult = true; // 是否生成结果
    private int generateResultCount; // 设置次数
    // 是否继续执行默认的系统行为, 
    // 如执行 doMessageSend方法的发送数据或者抛出异常, 后续操作.该参数只有在业务方法返回NULL时才有效.
    // 该标志在不同的服务实现中又不同的解释，请参考具体的服务实现解释.
    // 如果没有解释，则可以认为不被支持或为默认.
    private boolean executeDefault = true; 
    private TaskContext taskContext; // task上下文，该值有具体的实现设置，一旦设置，则不允许更改
    private BeforeSet againBeforeSet; // AGAIN
    private BeforeSet generateResultBeforeSet; // GENERATE RESULT
    private BeforeSet executeDefaultBeforeSet; // EXECUTE DEFAULT
    private IoSession session; // 关联的SOCKET session, 只有socket类才实现
    private ConcurrentTaskQueue<Task> resultRelaQueue; // 关联的Task, 如需要协调间的顺序
    private Collection<Task> childrens; // copy
    private Task parent; // PARENT
    private int priority = 0; // 处理优先级, 数字越大, 优先级越大, 默认为0(正常优先级), 优先级大的'可能'会被优先处理，具体处理方式视实现而定
    private Source source = Source.LOCAL;
    private Type type = Type.NORMAL;
    private Throwable throwable;
    private List<StateChangeListener> stateChangeListeners;
    
    public static enum Source {
    	LOCAL("LOCAL"),
    	REMOTE("REMOTE");
    	
        private String name;
    	
        Source(String name) {
    		this.name = name;
    	}
    	
    	public String getName() {
    		return this.name;
    	}
    	
    	@Override
    	public String toString() {
    		return this.name;
    	}
    }
    
    public static interface StateChangeListener extends Serializable {
    	/**
    	 * 改变
    	 * 
    	 * @param t
    	 * @param state
    	 */
    	public void changed(Task t, State state);
    }
    
    public static enum Type {
    	ACTIVE("ACTIVE"),
    	NORMAL("NORMAL"),
    	RESULT("RESULT");
    	
    	private String name;
    	
    	Type(String name) {
    		this.name = name;
    	}
    	
    	/**
    	 * 解析
    	 * 
    	 * @param name
    	 * @return
    	 */
    	public static Type parse(String name) {
    		name = StringUtil.trimWhiteToNull(name); 
    		if (name == null) {
    			return null;
    		}
    		    		   		
    		name = name.toUpperCase();
    		if (name.equals(ACTIVE.name)) {
    			return ACTIVE;
    		}
    		
    		if (name.equals(NORMAL.name)) {
    			return NORMAL;
    		}
    		
    		if (name.equals(RESULT.name)) {
    			return RESULT;
    		}
    		
    		return null;
    	}
    	
    	public String getName() {
    		return this.name;
    	}
    	
    	@Override
    	public String toString() {
    		return this.name;
    	}
    }
    
    /**
     * 增加listener
     * 
     * @param l
     */
    public void addStateChangeListener(StateChangeListener l) {
    	if (this.stateChangeListeners == null) {
    		this.stateChangeListeners = new ArrayList<StateChangeListener>();
    	}
    	
    	this.stateChangeListeners.add(l);
    }
    
    /**
     * 是否父类
     * 
     * @return
     */
    public boolean isParent() {
    	return createOrGetChildrens().size() > 0 ? true : false;
    }
    
    private Collection<Task> createOrGetChildrens() {
    	if (this.childrens == null) {
    		this.childrens = Collections.synchronizedList(new ArrayList<Task>());
    	}
    	return this.childrens;
    }
    
    /**
     * 获取父task
     * 
     * @return
     */
    public Task getParent() {
    	return this.parent;
    }
    
    /**
     * 被续传
     * 
     * @return
     */
    public boolean isResumed() {
    	if (isParent() && getState() == State.SUCCESS) {
    		Collection<Task> childrens = createOrGetChildrens();
    		for (Task t : childrens) {
    			if (t.route().traceTo().size() > this.route().traceTo().size()
    					&& t.route().traceFrom().size() == this.route().traceFrom().size()) {
    				return true;
    			}
    		}
    	}
    	return false;
    }
    
    /**
     * copy
     * 
     * @return
     */
    public Task copy() {
    	Task t = new Task(this.taskId);
    	
    	Object attach = this.attachement;
    	if (attach != null && (attach instanceof Cloneable)) {
    		try {
				attach = ClassUtil.clone(((Cloneable)attach));
			} catch (Throwable e) {
			}
    	}
    	
    	t.setAttachement(attach);
    	t.addAttributes(this.attributes);
    	t.setSession(this.session);
    	t.setSource(this.source);
    	t.setType(this.type);
    	
    	t.removeAttribute(FrameworkConstants.HAVA_SET_PENDRESULT);
    	t.removeAttribute(FrameworkConstants.TASK_IGNORE_AFTERPROCESSRESULT);
    	TaskUtil.clearAttrs(t);
    	t.route().from(route().from());
    	t.route().to(route().to());
    	t.parent = this;
    	
    	createOrGetChildrens().add(t);
    	    	
    	return t;
    }
    
    /**
     * 路由设置
     * 
     * @return
     */
    public Route route() {
    	return route;
    }
    
    @Override
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		
		buffer.append("Task: {").append("taskId=").append(taskId)
		      .append(", state=").append(state)
		      .append(", stateHistory=").append(stateHistory)
		      .append(", attachement=").append(attachement)
		      .append(", message=").append(message)
		      .append(", again=").append(again)
		      .append(", againCount=").append(againCount)
		      .append(", resumed=").append(isResumed())
		      .append(", againWaitTime=").append(waitTime)
		      .append(", againExclusive=").append(exclusive)
		      .append(", generateResult=").append(generateResult)
		      .append(", generateResultCount=").append(generateResultCount)
		      .append(", toUnique=").append(uniqueTo)
		      .append(", fromUnique=").append(uniqueFrom)
		      .append(", properties=").append(attributes)
		      .append(", route=").append(route)
		      .append(", source=").append(source)
		      .append(", type=").append(type)
		      .append("}");
		
		return buffer.toString();
	}
    
	public State getState() {
		return state;
	}

	public void setState(State state) {		
		this.state = state;
		synchronized (this.stateHistory) {
			this.stateHistory.add(new StateHistory(state, TimeUtil.getTime()));
		}		
		if (this.stateChangeListeners != null) {
			for (StateChangeListener l : this.stateChangeListeners) {
				l.changed(this, state);
			}
		}
	}

	public Task(String taskId) {
		if (taskId == null) {
			throw new RuntimeException("taskId must not be null.");
		}
		
    	this.taskId = taskId;  
    	this.attributes = new ConcurrentHashMap<String, Object>() {
    		private static final long serialVersionUID = 1L;
    		public Object put(String key, Object value) {
        		if (value == null) {
        			return null;
        		}
        		return super.put(key, value);
        	};
        };
    }
    
    /**
     * 测试是否还有任务
     * @return
     */
    public boolean hasMoreTask() {
    	return more == null ? false : more.hasMore();
    }
    
    public void setMore(More more) {
    	this.more = more;
    }

    /**
     * 获取任务ID
     * 
     * @return
     */
	public String getTaskId() {
		return taskId;
	}
	
	public String getToUnique() {
		if (uniqueTo == null) {
			if (taskId == null) {
				throw new UIPServiceException("taskId can't be null.");
			}
						
			if (route().to() == null) {
				throw new UIPServiceException("route to can't be null.");
			}
			
			uniqueTo = taskId + "-" + route().to();
		}
		
		return uniqueTo;
	}
	
	public String getFromUnique() {
		if (uniqueFrom == null) {
			if (taskId == null) {
				throw new UIPServiceException("taskId can't be null.");
			}
			
			if (route().from() == null) {
				throw new UIPServiceException("route from can't be null.");
			}
						
			uniqueFrom = taskId + "-" + route().from();
		}
		
		return uniqueFrom;
	}

	public void setTaskId(String taskId) {
		this.taskId = taskId;
	}

	public Map<String, Object> getAttributes() {
		return attributes;
	}

	public void setAttributes(Map<String, Object> attributes) {		
		this.attributes = attributes;
	}
	
	public void addAttributes(Map<String, Object> attributes) {		
		this.attributes.putAll(attributes);
	}
    	
	public Object setAttribute(String name, Object value) {		
		return this.attributes.put(name, value);
	}
	
	public Object getAttribute(String name) {
        return this.attributes.get(name);
	}
	
	public Object removeAttribute(String name) {		
		return this.attributes.remove(name);
	}
	
	public void clearAttributes() {		
		this.attributes.clear();
	}
	
	public Object getAttachement() {
		return attachement;
	}

	public void setAttachement(Object attachement) {
		this.attachement = attachement;
	}
    
	/**
	 * 测试是否有下一个任务节点的接口, 仅供Task类使用,
	 * 由各种应用场景按需要实现, 模拟Task位于一条任务链中,
	 * Task提供方法测试在任务链是否存在紧跟该任务的下一个任务.
	 */
	public static interface More extends Serializable {
		/**
		 * 测试队列是否还有下一个节点
		 * @return
		 */
		public boolean hasMore();
	}

	public String getMessage() {
		return message;
	}
	
	/**
	 * 描述
	 * 
	 * @param message
	 */
	public void setMessage(String message) {
		this.setMessage(message, null);
	}

	/**
	 * 描述
	 * 
	 * @param message
	 * @param t
	 */
	public void setMessage(String message, Throwable t) {
		if (message == null) {
			return;
		}
		
		this.message = message;			
		this.throwable = t;
	}

	public boolean isAgain() {		
		return again;
	}
    
	/**
	 * 设置控制task重复处理, setAgain方法处理模式。
	 * 1. 前提说明，again用于指明task应该被重复派至一个（RS），指明派至到真正派至到某个（RS），中途可以有一些属性控制
	 *    其行为。如控制延迟等待时间, 独占方式, 等.
	 * 2. 如果一个task在延迟等待, 可以通过两种方式将等待中断; a. 超时 b. 用TaskUtil.notifyTaskWaiter()方法唤醒.
	 * 3. 如果task需要多次进行重复指派, 一定要注意 声明again 与 中断 两个操作要串行成对出现,
	 *    即先 声明again, 再执行中断. 如果连续声明again，则后续声明都无效，只有第一次有效.
	 * 4. 由第3点描述可知，TaskUtil.notifyTaskWaiter()至多只会触发一次task操作。
	 *    如果task没有被声明again，则中断操作无效.
	 * 5. 如果不是正常的逻辑处理(不需要task正常地返回处理结果)，一定要执行
	 *      task.setExecuteDefault(false); // 不执行默认的系统行为
	 *	    task.setGenerateResult(false); // 不生成结果
	 *    ----- 一般应用在接收数据端，如doMessageReceived().
	 *    
	 *    如socket客户端, doMessageSend方法默认是不能返回null，如果要返回null,则
	 *     a. 设置task.setExecuteDefault(false);
	 *     b. 设置task.setAgain(true,.....) 
	 *    ----- 一般应用在发送数据端，如doMessageSend().
	 * 
	 * @param again     task重复处理
	 * @param waitTime  重复处理等待时间 (毫秒)
	 * @param exclusive 是否独占当前处理环境, 如果是独占, 中途不允许其他工单进入
	 * @param generateResult 是否生成task处理结果
	 */
	public void setAgain(boolean again, long waitTime, boolean exclusive, boolean generateResult) {
		if (this.againBeforeSet != null) {
			this.againBeforeSet.before();
		}
		
		this.again = again;
		this.waitTime = waitTime >= 0 ? waitTime : 0;
		this.exclusive = exclusive;
		this.againCount ++;
				
		setGenerateResult(generateResult);
	}

	public long getWaitTime() {
		return waitTime;
	}

	public boolean isExclusive(boolean again) {
		return again ? this.exclusive : false;
	}

	public int getAgainCount() {
		return againCount;
	}
	
	/*状态变更历史*/
	public static class StateHistory implements Serializable {
		private static final long serialVersionUID = 1L;
		private State state;
		private long time;
		
		public StateHistory(State state, long time) {
			this.state = state;
			this.time = time;
		}

		public State getState() {
			return state;
		}

		public long getTime() {
			return time;
		}
		
		@Override
		public String toString() {
			StringBuffer buffer = new StringBuffer();
			buffer.append("{StateHistory: state=")
			      .append(state).append(", time=").append(time)
			      .append("}");
			return buffer.toString();
		}
	}

	public List<StateHistory> getStateHistory() {
		return stateHistory;
	}

	public boolean isGenerateResult() {
		return generateResult;
	}

	public void setGenerateResult(boolean generateResult) {
		if (this.generateResultBeforeSet != null) {
			this.generateResultBeforeSet.before();
		}
		
		this.generateResult = generateResult;
		this.generateResultCount ++;
	}

	public int getGenerateResultCount() {
		return generateResultCount;
	}

	public boolean isExecuteDefault() {
		return executeDefault;
	}

	/**
	 * 用于修改当需要的结果与默认结果不一致时，定制修改系统行为。
	 * 需要的结果与默认结果不一致为：
	 * 1. 默认期望返回非null对象，但此时你需要返回null对象，需要将用该方法置为false
	 * 2. 默认期望生成处理结果，但此时你不需要结果，且不愿意改变当前的处理环境上下文，此时需要将用该方法置为false
	 * @param executeDefault
	 */
	public void setExecuteDefault(boolean executeDefault) {
		if (this.executeDefaultBeforeSet != null) {
			this.executeDefaultBeforeSet.before();
		}
		this.executeDefault = executeDefault;
	}

	/**
	 * task上下文
	 * 
	 * @return
	 */
	public TaskContext getTaskContext() {		
		return this.taskContext;
	}

	public void setTaskContext(TaskContext taskContext) {
		if (this.taskContext != null) {
			throw new RuntimeException("the taskContext has been set, can't be altered.");
		}
		this.taskContext = taskContext;
	}
	
	/**
	 * setAgain方法调用之前处理
	 * 
	 * @param before
	 */
	public void beforeSetAgain(BeforeSet before) {
		this.againBeforeSet = before;
	}
	
	/**
	 * setGenerateResult方法调用之前处理
	 * 
	 * @param before
	 */
	public void beforeGenerateResult(BeforeSet before) {
		this.generateResultBeforeSet = before;
	}
	
	/**
	 * setExecuteDefault方法调用之前处理
	 * 
	 * @param before
	 */
	public void beforeExecuteDefault(BeforeSet before) {
		this.executeDefaultBeforeSet = before;
	}

	/**
	 * 关联的SOCKET链接, 一般情况下，只允许业务层使用IoSession对象的保存属性方法，其他方法在未定义情况下不允许使用
	 * 
	 * @return
	 */
	public IoSession getSession() {
		if (this.session == null) {
			throw new UnsupportedOperationException("该类型接口服务不支持该方法调用");
		}
		return this.session;
	}

	public void setSession(IoSession session) {
		this.session = session;
	}

	/**
	 * 关联的task
	 * 
	 * @return
	 */
	public ConcurrentTaskQueue<Task> getRela() {
		return this.resultRelaQueue;
	}

	public void addRela(Task rela) {
		if (this.resultRelaQueue == null) {
			this.resultRelaQueue = new ConcurrentTaskQueue<Task>();
		}
		this.resultRelaQueue.offer(rela);
	}

	public int getPriority() {
		return priority;
	}

	public void setPriority(int priority) {
		this.priority = priority;
	}
	
	public boolean isNormalPriority() {
		return this.priority == 0 ? true : false;
	}

	/**
	 * 来源
	 * 
	 * @return
	 */
	public Source getSource() {
		return this.source;
	}

	public void setSource(Source source) {
		this.source = source;
	}

	/**
	 * 类型
	 * 
	 * @return
	 */
	public Type getType() {
		return type;
	}

	public void setType(Type type) {
		this.type = type;
	}

	public Throwable getThrowable() {
		return throwable;
	}

	public void setThrowable(Throwable throwable) {
		this.throwable = throwable;
	}
}
